package multi.SAUNA;

import java.awt.Graphics2D;
import java.util.Vector;

import meta.symbolsAndTime.SAUNA.Map;
import org.metacsp.multi.spatial.rectangleAlgebra.Point;

public class Line {
	
	//line types
	public static enum LINE_REPRESENTATION {X, Y, EXPLICIT};

	
	public double m, q;
	public double[] xBounds;
	private LINE_REPRESENTATION lr;
	private double x, y;
	public Line(double m, double q) {
		this.m = m;
		this.q = q;
		xBounds = new double[] {0, 100};
		this.lr = LINE_REPRESENTATION.EXPLICIT;
	}
	
	public Line(Point p1, Point p2) {
		this((p2.y-p1.y)/(p2.x-p1.x),p1.y-(p1.x*(p2.y-p1.y)/(p2.x-p1.x)));
		if (p1.x - p2.x == 0) {
			this.lr = LINE_REPRESENTATION.X;
			this.x = p1.x;
		}
		else if (p1.y - p2.y == 0) {
			this.lr = LINE_REPRESENTATION.Y;
			this.y = p1.y;
		}
	}
	
	public Line getPerpendicular(Point point) {
		if (this.lr.equals(LINE_REPRESENTATION.EXPLICIT)) {
			double mP = -1/m;
			double qP = point.y-(mP*point.x);
			return new Line(mP,qP);
		}
		if (this.lr.equals(LINE_REPRESENTATION.X)) {
			return new Line(new Point(0,point.y), new Point(1,point.y));
		}
		if (this.lr.equals(LINE_REPRESENTATION.Y)) {
			return new Line(new Point(point.x,0), new Point(point.x,1));
		}
		return null;
	}
	
	public Line getTranslation(double trans) {
		double alpha, deltaQ = 0;
		if (this.lr.equals(LINE_REPRESENTATION.EXPLICIT) && m > 0) {
			alpha = Math.PI/2 - Math.atan(m);
			deltaQ = trans/Math.sin(alpha);
			return new Line(m,q+deltaQ);
		}
		if (this.lr.equals(LINE_REPRESENTATION.EXPLICIT) && m < 0) {
			alpha = Math.PI/2 - Math.atan(-1/m);
			deltaQ = trans/Math.cos(alpha);
			return new Line(m,q+deltaQ);
		}
		if (this.lr.equals(LINE_REPRESENTATION.Y)) {
			return new Line(new Point(0,y+trans),new Point(1,y+trans));
		}
		if (this.lr.equals(LINE_REPRESENTATION.X)) {
			return new Line(new Point(x+trans,0),new Point(x+trans,1));
		}
		return null;	
	}
	
	public Point getIntersection(Line line) {
		if (this.lr.equals(LINE_REPRESENTATION.X) && line.lr.equals(LINE_REPRESENTATION.Y)) {
			return new Point(x,line.y);
		}
		if (line.lr.equals(LINE_REPRESENTATION.X) && this.lr.equals(LINE_REPRESENTATION.Y)) {
			return new Point(line.x,y);
		}
		if (this.lr.equals(LINE_REPRESENTATION.Y) && line.lr.equals(LINE_REPRESENTATION.EXPLICIT)) {
			return new Point(line.getX(y),y);
		}
		if (this.lr.equals(LINE_REPRESENTATION.X) && line.lr.equals(LINE_REPRESENTATION.EXPLICIT)) {
			return new Point(x,line.getY(x));
		}
		if (line.lr.equals(LINE_REPRESENTATION.Y) && this.lr.equals(LINE_REPRESENTATION.EXPLICIT)) {
			return new Point(getX(line.y),line.y);
		}
		if (line.lr.equals(LINE_REPRESENTATION.X) && this.lr.equals(LINE_REPRESENTATION.EXPLICIT)) {
			return new Point(line.x,getY(line.x));
		}
		return new Point((line.q-q)/(m-line.m),m*(line.q-q)/(m-line.m) + q);
	}
	
	public double getY(double x) {
		if (lr.equals(LINE_REPRESENTATION.Y)) return y;
		return m*x+q;
	}
	
	public double getX(double y) {
		if (lr.equals(LINE_REPRESENTATION.X)) return x;
		return (y-q)/m;
	}
	
	public Point[] sample(Point start, Point end, double precision) {
		double delta = precision;
		Vector<Point> ret = new Vector<Point>();
		ret.add(start);
		Line perpendicular = this.getPerpendicular(start);			
		Point newPoint;
		if (this.lr.equals(LINE_REPRESENTATION.EXPLICIT) && start.x < end.x) {
			if (m > 0) {
				while ((newPoint = perpendicular.getTranslation(delta).getIntersection(this)).x < end.x) {
					ret.add(newPoint);
					delta += precision;
				}
			}
			else {
				while ((newPoint = perpendicular.getTranslation(-delta).getIntersection(this)).x < end.x) {
					ret.add(newPoint);
					delta += precision;
				}
			}
		}
		else if (this.lr.equals(LINE_REPRESENTATION.EXPLICIT) && start.x > end.x) {
			if (m > 0) {
				while ((newPoint = perpendicular.getTranslation(-delta).getIntersection(this)).x > end.x) {
					ret.add(newPoint);
					delta += precision;
				}
			}
			else {
				while ((newPoint = perpendicular.getTranslation(delta).getIntersection(this)).x > end.x) {
					ret.add(newPoint);
					delta += precision;
				}
			}
		}
		else if (this.lr.equals(LINE_REPRESENTATION.X) && start.y < end.y) {
			while ((newPoint = perpendicular.getTranslation(delta).getIntersection(this)).y < end.y) {
				ret.add(newPoint);
				delta += precision;
			}
		}
		else if (this.lr.equals(LINE_REPRESENTATION.X) && start.y > end.y) {
			while ((newPoint = perpendicular.getTranslation(-delta).getIntersection(this)).y > end.y) {
				ret.add(newPoint);
				delta += precision;
			}
		}
		else if (this.lr.equals(LINE_REPRESENTATION.Y) && start.x < end.x) {
			while ((newPoint = perpendicular.getTranslation(delta).getIntersection(this)).x < end.x) {
				ret.add(newPoint);
				delta += precision;
			}
		}
		else if (this.lr.equals(LINE_REPRESENTATION.Y) && start.x > end.x) {
			while ((newPoint = perpendicular.getTranslation(-delta).getIntersection(this)).x > end.x) {
				ret.add(newPoint);
				delta += precision;
			}
		}
		ret.add(end);
		return ret.toArray(new Point[ret.size()]);
	}
	
	public String toString() {
		if (this.lr.equals(LINE_REPRESENTATION.EXPLICIT))
			return "y = " + m + "x + " + q;
		if (this.lr.equals(LINE_REPRESENTATION.Y))
			return "y = " + y;
		if (this.lr.equals(LINE_REPRESENTATION.X))
			return "x = " + x;
		return null;
	}
	
	public void draw(Graphics2D g, Map map) {
		Point p1 = map.toScreen(xBounds[0],this.getY(xBounds[0]));
		Point p2 = map.toScreen(xBounds[1],this.getY(xBounds[1]));
		g.drawLine(p1.x(), p1.y(), p2.x(), p2.y());					
	}

}	
